/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package io.iec.edp.caf.multicontext.analysis;

import io.iec.edp.caf.commons.runtime.CafEnvironment;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;


import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class StartupAnalysisLog {
    //bean初始化各个阶段的时间
    private static Map<String, BeanStepTimeEntity> timeMap = new HashMap<>();
    //各模块的bean统计
    private static Map<String, Integer> beanCountMap = new HashMap<>();
    //各模块rest bean统计
    private static Map<String, List<String>> restBeanMap = new HashMap<>();
    //各模块rpc bean统计
    private static Map<String, List<String>> rpcBeanMap = new HashMap<>();

    private static List<String> beanTime = new ArrayList<>();

    private static Map<String, Map<String, BeanDefinition>> beanMap = new HashMap<>();

    private static ApplicationContext applicationContext;
//    private static DefaultListableBeanFactory beanFactory;

    public static void logNoParallel(){
        logBeanDef("非并行当前bean总数,"+applicationContext.getBeanDefinitionCount(),"bean/all_bean分析.csv",true,false);
    }

    public static void setApplicationContext(ApplicationContext context){
        applicationContext = context;
//        beanFactory = (DefaultListableBeanFactory) ((AnnotationConfigServletWebServerApplicationContext)context).getBeanFactory();
    }

    //记录bean初始化每步操作的时间
    public static void countBeanStepTime(String moduleName,String stage,Long cost){
        try {
            var env = applicationContext.getBean(Environment.class);
            var isLog = env.getProperty("startup.log",boolean.class);
            if(isLog!=null && isLog) {
                if(timeMap.get(moduleName)==null){
                    BeanStepTimeEntity b = new BeanStepTimeEntity();
                    b.getTimeMap().put(stage,cost);
                    b.setTotal(cost);
                    timeMap.put(moduleName,b);
                }else{
                    var b = timeMap.get(moduleName);
                    var readyTime = b.getTotal();
                    var newreadytime = readyTime+cost;
                    b.getTimeMap().put(stage,cost);
                    b.setTotal(newreadytime);
                    timeMap.put(moduleName,b);
                }
            }
        }catch (Throwable e){
            if(log.isWarnEnabled()) log.warn("CountBeanStepTime Error.",e);
        }

    }

    //计数rest bean
    public static void countRestBean(String moduleName,String beanName,String beanType){
        var env = applicationContext.getBean(Environment.class);
        var isLog = env.getProperty("startup.log",boolean.class);
        if(isLog!=null && isLog) {
            var rest = restBeanMap.get(moduleName);
            if(rest==null){
                rest = new ArrayList<>();
            }
            rest.add(beanName+","+beanType);
            restBeanMap.put(moduleName,rest);
        }

    }

    //计数rpc bean
    public static void countRpcBean(String moduleName,String beanName,String beanType){
        var env = applicationContext.getBean(Environment.class);
        var isLog = env.getProperty("startup.log",boolean.class);
        if(isLog!=null && isLog) {
            var rest = rpcBeanMap.get(moduleName);
            if(rest==null){
                rest = new ArrayList<>();
            }
            rest.add(beanName+","+beanType);
            rpcBeanMap.put(moduleName,rest);
        }

    }

    //计数模块bean总数
    public static void countBeanCountMap(String moduleName, Integer count){
        var env = applicationContext.getBean(Environment.class);
        var isLog = env.getProperty("startup.log",boolean.class);
        if(isLog!=null && isLog) {
            beanCountMap.put(moduleName,count);
        }
    }
    //
    public static void countBeanTime(String beanTimeDes){
        var env = applicationContext.getBean(Environment.class);
        var isLog = env.getProperty("startup.log",boolean.class);
        if(isLog!=null && isLog) {
            beanTime.add(beanTimeDes);
        }
    }

    public static void countBeanMap(String moduleName, ConfigurableListableBeanFactory beanFactory) {
        var env = beanFactory.getBean(Environment.class);
        var isLog = env.getProperty("startup.log",boolean.class);
        if(isLog!=null && isLog) {
            var beanF = beanFactory;
            Field beanDefinitionMap = null;
            try {
                beanDefinitionMap = DefaultListableBeanFactory.class.getDeclaredField("beanDefinitionMap");
                beanDefinitionMap.setAccessible(true);
                var beandefmap = (Map<String, BeanDefinition>) beanDefinitionMap.get(beanF);
                beanMap.put(moduleName,beandefmap);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }


    }

    public static void clearBeanAnaylsis(){
        timeMap.clear();
        beanCountMap.clear();
        restBeanMap.clear();
        rpcBeanMap.clear();
        beanTime.clear();
        beanMap.clear();
    }

    //记录bean各个步骤
    public static void logBeanStepTime(){

//        final Long[] count = {0L};
        timeMap.forEach((x,y)->{
            BeanStepTimeEntity b = (BeanStepTimeEntity)y;
            var beancount = beanCountMap.get(x);
//            count[0] += beancount;
//            log.info("模块"+x+"的bean总数："+beancount);
            logBeanDef("模块"+x+"的bean总数,"+beancount,"bean/"+x+"/bean_step_time分析.csv",true,false);

//            log.info("模块"+x+"总耗时："+b.getTotal()+"ms");
            logBeanDef("模块"+x+"总耗时,"+b.getTotal()+"ms","bean/"+x+"/bean_step_time分析.csv",true,false);

            b.getTimeMap().forEach((k,v)->{
//                log.info("模块"+x+"["+k+"]"+"耗时："+v+"ms");
                logBeanDef("模块"+x+"["+k+"]"+"耗时,"+v+"ms","bean/"+x+"/bean_step_time分析.csv",true,false);

            });
        });
//        logBeanDef("当前系统bean总数,"+ count[0],"bean/bean_step_time分析.csv",true,false);

    }

    //分析rpc bean
    public static void logRpcBeanAnalysis(){
        rpcBeanMap.forEach((x,y)->{
            logBeanDef("当前模块"+x+"的rpc bean总数，"+y.size(),"bean/"+x+"/rpc_bean分析.csv",true,false);
            logBeanDef("beanName,beanClass","bean/"+x+"/rpc_bean分析.csv",true,false);
            y.forEach(z->{
                logBeanDef(z,"bean/"+x+"/rpc_bean分析.csv",true,false);
            });
        });
    }

    //分析rest bean
    public static void logRestBeanAnalysis(){
        restBeanMap.forEach((x,y)->{
            logBeanDef("当前模块"+x+"的rest bean总数，"+y.size(),"bean/"+x+"/rest_bean分析.csv",true,false);
            logBeanDef("beanName,beanClass","bean/"+x+"/rpc_bean分析.csv",true,false);
            y.forEach(z->{
                logBeanDef(z,"bean/"+x+"/rest_bean分析.csv",true,false);
            });
        });
    }

    //记录所有bean的分析，bean太多了，所以放到启动过程中写入文件
    public static void logBeanAnalysis(){
        final int[] beancount = {0};
        logBeanDef("模块,bean个数","bean/all_bean分析.csv",true,false);

        beanMap.forEach((x1,y1)->{
            beancount[0] += y1.size();

            logBeanDef("bean总数,"+y1.size(),"bean/"+x1+"/bean分析.csv",true,false);
            logBeanDef("beanName,bean信息","bean/"+x1+"/bean分析.csv",true,false);
            y1.forEach((x,y)->{

                if(y.getSource() instanceof MethodMetadata){
                    var meta  = (MethodMetadata)y.getSource();
                    logBeanDef(x+","+meta.getDeclaringClassName()+":"+meta.getReturnTypeName()+":"+meta.getMethodName(),"bean/"+x1+"/bean分析.csv",true,false);
                }else if(y.getSource() instanceof AnnotationMetadata){
                    var meta  = ((AnnotationMetadata)y.getSource());
                    logBeanDef(x+","+meta.getClassName(),"bean/"+x1+"/bean分析.csv",true,false);

                }else{
                    if(y.getSource()!=null){
                        if(y.getSource().getClass()!=null){
                            logBeanDef(x+","+y.getSource().getClass().getTypeName(),"bean/"+x1+"/bean分析.csv",true,false);
                        }else{
                            logBeanDef(x+","+y.getSource().toString(),"bean/"+x1+"/bean分析.csv",true,false);
                        }
                    }else{
                        logBeanDef(x,"bean/"+x1+"/bean分析.csv",true,false);

                    }
                }
            });

            logBeanDef(x1+","+y1.size(),"bean/all_bean分析.csv",true,false);
        });

        logBeanDef("合计,"+beancount[0],"bean/all_bean分析.csv",true,false);
    }

    //记录bean初始化时间
    public static void logBeanTimeAnalysis(){
        beanTime.forEach(x->{
            logBeanDef(x,"bean/all_bean_time分析.csv",true,false);
        });
    }


    private static void logBeanDef(Object data,String filename,boolean append,boolean isNewLine) {
//        var env = beanFactory.getBean(Environment.class);
//        var isLog = env.getProperty("startup.log",boolean.class);
//        if(isLog!=null && isLog){
            try{
                if(data!=null){
                    var root = CafEnvironment.getServerRTPath();
//                    var root = EnvironmentUtil.getServerRTPath();
                    String fileFullPath = root+"/"+filename;
                    var index = fileFullPath.lastIndexOf("/");
                    String dirFullPath = fileFullPath.substring(0,index);


                    File dataFile =new File(fileFullPath);
                    File dirFile = new File(dirFullPath);

                    if (!dirFile.exists()) {
                        dirFile.mkdirs();
                    }

                    Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dataFile,append), "UTF-8"));
                    out.write((String)data);
                    out.write("\r\n");  //换行
                    if(isNewLine){
                        out.write("\r\n");
                    }
                    out.close();
                }

            }
            catch(Throwable e){
                log.info("日志出错",e);
            }

//        }
    }

}
